 //*************************************************************************
//
//  File Name	: main.cpp
//  Author		: Nguyen Ho Man Rang
//  Created		: March 2012 
//
//  Description	: Lab 2/2.
//  
//*************************************************************************

#include <math.h>
#include <gl/glut.h>

#define PI 3.14159;


//  define the window position on screen
int wx;
int wy;

//  variables representing the window size
int ww = 640;
int wh = 480;

//  variable representing the window title
char *wtitle = "Lab 3";


float cam_x = 0.0f, cam_y = 0.0f, cam_z =0.0f;
float angle = 0;
bool split = false;
bool perspective = false;

// store the present view matrix
float m[16];
//  Initialization
void init(void)
{
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(2, 2, 2, 0, 0, 0, 0, 1, 0);
	glGetFloatv(GL_MODELVIEW_MATRIX, m);
	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

void changeProjection()
{
	GLdouble v;	
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
	if (ww > wh)
	{
		v = (GLdouble) ww/wh;
		if (perspective)
			glFrustum(-2.0*v, 2.0*v, -2.0, 2.0, 1, 10);
		else
			glOrtho(-2.0*v, 2.0*v, -2.0, 2.0, 1, 10);
	}
	else
	{
		v = (GLdouble) wh/ww;
		if (perspective)
			glFrustum(-2.0, 2.0, -2.0*v, 2.0*v, 1, 10);
		else
			glOrtho(-2.0, 2.0, -2.0*v, 2.0*v, 1, 10);
	}

    glMatrixMode(GL_MODELVIEW);
	glutPostRedisplay();
}

//-------------------------------------------------------------------------
//  draw the coordinate system
//-------------------------------------------------------------------------
void drawAxis()
{
	glBegin(GL_LINES);
            glColor3f(1.0f,0.0f,0.0f); //x is red
            glVertex3i(2,0,0);
            glVertex3i(-2,0,0);
            glColor3f(0.0f,1.0f,0.0f); //x is green
            glVertex3i(0,2,0);
            glVertex3i(0,-2,0);
            glColor3f(0.0f,0.0f,1.0f); //z is blue
            glVertex3i(0,0,2);
            glVertex3i(0,0,-2);
    glEnd();
}
//-------------------------------------------------------------------------
//  Main window reshape function
//-------------------------------------------------------------------------
void reshape (int w, int h)
{
	ww = w;
	wh = h;
    changeProjection();
}
//-------------------------------------------------------------------------
// Function to calculate the present view matrix
//-------------------------------------------------------------------------
void calculateViewMatrix(float angle, float x, float y, float z)
{
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glRotatef(angle, x, y, z);
	glMultMatrixf(m);
	glGetFloatv(GL_MODELVIEW_MATRIX, m);
}

//-------------------------------------------------------------------------
//  Main window reshape function
//-------------------------------------------------------------------------
void keyboard (unsigned char key, int x, int y)
{
    switch(key)
    {
	case 'a':
		calculateViewMatrix(1.0, 1, 0, 0);
		glutPostRedisplay();
		break;
	case 'A':
		calculateViewMatrix(-1.0, 1, 0, 0);
		glutPostRedisplay();
		break;
	case 'b':
		calculateViewMatrix(1.0, 0, 1, 0);
		glutPostRedisplay();
		break;
	case 'B':
		calculateViewMatrix(-1.0, 0, 1, 0);
		glutPostRedisplay();
		break;
	case 'c':
		calculateViewMatrix(1.0, 0, 0, 1);
		glutPostRedisplay();
		break;
	case 'C':
		calculateViewMatrix(-1.0, 0, 0, 1);
		glutPostRedisplay();
		break;
	case 'x':
		cam_x += 0.1;		
		glutPostRedisplay();
		break;
	case 'X':
		cam_x -= 0.1;		
		glutPostRedisplay();
		break;
	case 'y':
		cam_y += 0.1;		
		glutPostRedisplay();
		break;
	case 'Y':
		cam_y -= 0.1;		
		glutPostRedisplay();
		break;
	case 'z':
		cam_z += 0.1;		
		glutPostRedisplay();
		break;
	case 'Z':
		cam_z -= 0.1;		
		glutPostRedisplay();
		break;
	case 'p':
		perspective =!perspective;
		changeProjection();
		break;
	case 'q':
		exit(0);
	case 's':
		split = !split;
		glutPostRedisplay();
    }
}
//-------------------------------------------------------------------------
//  The function to draw a box
//-------------------------------------------------------------------------
void drawSquarePyramid(float h, float s)
{
	//draw front side
	glColor3f(1, 0, 0);
	glBegin(GL_TRIANGLES);
		glVertex3f(0, 0, 0);
		glVertex3f(-s, h, s);
		glVertex3f(s, h, s);		
	glEnd();

	//draw back side
	glColor3f(0, 1, 0);
	glBegin(GL_TRIANGLES);
		glVertex3f(0, 0, 0);
		glVertex3f(-s, h, -s);
		glVertex3f(s, h, -s);		
	glEnd();

	//draw right side
	glColor3f(0, 0, 1);
	glBegin(GL_TRIANGLES);
		glVertex3f(0, 0, 0);
		glVertex3f(s, h, -s);
		glVertex3f(s, h, s);		
	glEnd();

	//draw left side
	glColor3f(1, 1, 0);
	glBegin(GL_TRIANGLES);
		glVertex3f(0, 0, 0);
		glVertex3f(-s, h, s);
		glVertex3f(-s, h, -s);		
	glEnd();

	//draw top side
	glColor3f(0, 1, 1);
	glBegin(GL_QUADS);
		glVertex3f(-s, h, -s);
		glVertex3f(-s, h, s);
		glVertex3f(s, h, s);
		glVertex3f(s, h, -s);
	glEnd();
}

//-------------------------------------------------------------------------
//  The function to draw a box
//-------------------------------------------------------------------------
void drawBox(float sx, float sy, float sz)
{
	//draw front side
	glColor3f(1, 0, 0);
	glBegin(GL_QUADS);
		glVertex3f(-sx, -sy, sz);
		glVertex3f(sx, -sy, sz);
		glVertex3f(sx, sy, sz);
		glVertex3f(-sx, sy, sz);
	glEnd();

	//draw back side
	glColor3f(0, 1, 0);
	glBegin(GL_QUADS);		
		glVertex3f(-sx, -sy, -sz);
		glVertex3f(-sx, sy, -sz);
		glVertex3f(sx, sy, -sz);
		glVertex3f(sx, -sy, -sz);
	glEnd();

	//draw left side
	glColor3f(0, 0, 1);
	glBegin(GL_QUADS);		
		glVertex3f(-sx, -sy, -sz);
		glVertex3f(-sx, -sy, sz);
		glVertex3f(-sx, sy, sz);
		glVertex3f(-sx, sy, -sz);
	glEnd();

	//draw right side
	glColor3f(1, 1, 0);
	glBegin(GL_QUADS);		
		glVertex3f(sx, -sy, -sz);
		glVertex3f(sx, sy, -sz);
		glVertex3f(sx, sy, sz);
		glVertex3f(sx, -sy, sz);
	glEnd();

	//draw top side
	glColor3f(1, 0, 1);
	glBegin(GL_QUADS);		
		glVertex3f(-sx, sy, -sz);
		glVertex3f(-sx, sy, sz);
		glVertex3f(sx, sy, sz);
		glVertex3f(sx, sy, -sz);
	glEnd();

	//draw top side
	glColor3f(0, 1, 1);
	glBegin(GL_QUADS);		
		glVertex3f(-sx, -sy, -sz);
		glVertex3f(sx, -sy, -sz);
		glVertex3f(sx, -sy, sz);
		glVertex3f(-sx, -sy, sz);
	glEnd();
}

void drawScence()
{
	drawAxis();	
	glTranslatef(0.6, 0.2 , 0);

	glPushMatrix();
	glRotatef(-angle, 0, 1, 0); 
	drawBox(0.2, 0.2, 0.2);
	glPopMatrix();

	glTranslatef(0, 0.2, 0);
	glRotatef(angle, 0, 1, 0);
	//glRotatef(45, 1, 0 ,1);
	drawSquarePyramid(0.2, 0.15);
}
void setPYR(float pitch, float yaw, float roll) 
{
    glRotatef(roll, 0, 0, 1);
	glRotatef(yaw, 0, 1, 0);
	glRotatef(pitch, 1, 0, 0);
        
}

//-------------------------------------------------------------------------
//  Call display on the window
//-------------------------------------------------------------------------
void display ()
{
    //  Clear window
	glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	if(split)
	{
		glEnable(GL_SCISSOR_TEST);

		glViewport(0, 0, (GLsizei) ww/2, (GLsizei) wh/2);
		//  Set camera position and view vector
		glMatrixMode (GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt (2, 0, 0, 0, 0, 0, 0, 1, 0);
		drawScence();

		glViewport(0, (GLsizei) wh/2, (GLsizei) ww/2, (GLsizei) wh/2);
		//  Set camera position and view vector
		glMatrixMode (GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt (0, 2, 0, 0, 0, 0, 1, 0, 0);
		drawScence();

		glViewport((GLsizei) ww/2, (GLsizei) wh/2, (GLsizei) ww/2, (GLsizei) wh/2);
		//  Set camera position and view vector
		glMatrixMode (GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt (0, 0, 2, 0, 0, 0, 0, 1, 0);
		drawScence();

		glViewport((GLsizei) ww/2, 0, (GLsizei) ww/2, (GLsizei) wh/2);
		//  Set camera position and view vector
		glMatrixMode (GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt (2, 2, 2, 0,  0, 0, 0, 1, 0);		
		drawScence();

		glDisable(GL_SCISSOR_TEST);
	}
	else
	{
		glViewport(0, 0, (GLsizei) ww, (GLsizei) wh);
		glMatrixMode (GL_MODELVIEW);
		glLoadIdentity();
		glLoadMatrixf(m);
		glTranslatef(-cam_x, -cam_y, -cam_z);
		drawScence();		
	}    
	
	// Flush Object
	glFlush();
    
    glutSwapBuffers ();
}


//-------------------------------------------------------------------------
// function used when the user is dragging the mouse
//-------------------------------------------------------------------------
void processTimer(int value)
{
	angle += value;
	glutTimerFunc(100, processTimer, 1);
	glutPostRedisplay();
}

//-------------------------------------------------------------------------
//  This function sets the window x and y coordinates
//  such that the window becomes centered
//-------------------------------------------------------------------------
void centerOnScreen ()
{
    wx = (glutGet (GLUT_SCREEN_WIDTH) - ww)/2;
    wy = (glutGet (GLUT_SCREEN_HEIGHT) - wh)/2;
}

//-------------------------------------------------------------------------
//  Program Main method.
//-------------------------------------------------------------------------
int main (int argc, char **argv)
{
    //  Connect to the windowing system
    glutInit(&argc, argv);

    //  create a window with the specified dimensions
    glutInitWindowSize (ww, wh);

    //  Set the window x and y coordinates such that the 
    //  window becomes centered
    centerOnScreen ();

    //  Position Window
    glutInitWindowPosition (wx, wy);

    //  Set Display mode
    glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE);

    //  Create window with the specified title
    glutCreateWindow (wtitle);

	//  Initialize parameter
	init();

    // Set the callback functions for main window
    glutDisplayFunc (display);
    glutReshapeFunc  (reshape);
    glutKeyboardFunc (keyboard);
	glutTimerFunc(100, processTimer, 1);
   

    //  Start GLUT event processing loop
    glutMainLoop();
	return 1;
}

